﻿using System;
using System.IO;
using System.Web;
using System.Windows.Media.Imaging;
using Popw.LiveBlog.Repositories;

namespace Popw.LiveBlog.Services
{
	public interface IImageService
	{
		Uri SaveImage(byte[] bytes, string fileName);
		Uri SaveImage(HttpPostedFileBase postedFile);
		byte[] ConstrainResize(byte[] bytes, int maxWidth, int maxHeight, int qualityLevel);
	}

	public class ImageService : IImageService
	{
		private readonly IImageRepo _imageRepo;

		public ImageService(IImageRepo imageRepo)
		{
			_imageRepo = imageRepo;
		}

		public Uri SaveImage(byte[] bytes, string fileName)
		{
			if (bytes != null && bytes.Length > 0)
			{
				var width = Config.Settings.WidthConstraint;
				var height = Config.Settings.HeightConstraint;
				bytes = ConstrainResize(bytes, width, height, 70);
				var imageID = _imageRepo.SaveToDatabase(fileName);
				var uri = new Uri(String.Format(Config.Settings.ImageUrlFormat, _imageRepo.BlobContainerName, imageID));
				_imageRepo.SaveToCdn(bytes, imageID + ".jpg");
				return uri;
			}
			return null;
		}

		public Uri SaveImage(HttpPostedFileBase postedFile)
		{
			if (postedFile != null && postedFile.ContentLength > 0)
			{
				var width = Config.Settings.WidthConstraint;
				var height = Config.Settings.HeightConstraint;
				var bytes = ConstrainResize(GetBytes(postedFile), width, height, 70);
				var imageID = _imageRepo.SaveToDatabase(postedFile.FileName);
				var uri = new Uri(String.Format(Config.Settings.ImageUrlFormat, _imageRepo.BlobContainerName, imageID));
				_imageRepo.SaveToCdn(bytes, imageID + ".jpg");
				return uri;
			}
			return null;
		}

		public byte[] ConstrainResize(byte[] bytes, int maxWidth, int maxHeight, int qualityLevel)
		{
			var originalImage = new BitmapImage();
			originalImage.BeginInit();
			originalImage.StreamSource = new MemoryStream(bytes);
			originalImage.CreateOptions = BitmapCreateOptions.IgnoreColorProfile | BitmapCreateOptions.PreservePixelFormat;
			originalImage.EndInit();
			int newHeight;
			int newWidth;
			CalculateResize(maxWidth, maxHeight, originalImage.PixelWidth, originalImage.PixelHeight, out newWidth, out newHeight);
			var image = Resize(bytes, newWidth, newHeight, qualityLevel);
			return image;
		}

		private static void CalculateResize(int maxWidth, int maxHeight, int originalWidth, int originalHeight, out int newWidth, out int newHeight)
		{
			newWidth = originalWidth;
			newHeight = originalHeight;
			if (originalHeight < maxHeight && originalWidth < maxWidth)
				return;
			var xRatio = (double)maxWidth / originalWidth;
			var yRatio = (double)maxHeight / originalHeight;
			var ratio = Math.Min(xRatio, yRatio);
			newWidth = (int)(originalWidth * ratio);
			newHeight = (int)(originalHeight * ratio);
		}

		private static byte[] Resize(byte[] bytes, int? width, int? height, int qualityLevel)
		{
			if (bytes == null)
				throw new Exception("Bytes parameter is null.");
			var image = new BitmapImage();
			image.BeginInit();
			if (width.HasValue)
				image.DecodePixelWidth = width.Value;
			if (height.HasValue)
				image.DecodePixelHeight = height.Value;
			image.StreamSource = new MemoryStream(bytes);
			image.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
			image.EndInit();
			var transform = new TransformedBitmap();
			transform.BeginInit();
			transform.Source = image;
			transform.EndInit();
			return ToJpegBytes(transform, qualityLevel);
		}

		private static byte[] ToJpegBytes(BitmapSource image, int qualityLevel)
		{
			if (image == null)
				throw new Exception("Image parameter is null.");
			var encoder = new JpegBitmapEncoder();
			var stream = new MemoryStream();
			encoder.Frames.Add(BitmapFrame.Create(image));
			encoder.QualityLevel = qualityLevel;
			encoder.Save(stream);
			var length = (int)stream.Length;
			var imageData = new byte[length];
			stream.Position = 0;
			stream.Read(imageData, 0, length);
			return imageData;
		}

		private static byte[] GetBytes(HttpPostedFileBase file)
		{
			var length = (int)file.InputStream.Length;
			var bytes = new byte[length];
			file.InputStream.Read(bytes, 0, length);
			return bytes;
		} 
	}
}